import re
import json
import allure
from typing import Union, Any, Dict, List
from urllib import parse
from playwright.sync_api import Page, APIResponse

class RequestKeywords:
    def __init__(self, page: Page):
        self.page = page

    def get(self, url: str, **kwargs):
        """发送GET请求"""
        with allure.step(f"发送GET请求: {url}"):
            try:
                response: APIResponse = self.page.context.request.get(url, **kwargs)
                print(f"PASS|GET请求成功: {url}")
                return smart_response_handler(response)
            except Exception as e:
                print(f"FAIL|GET请求失败: {e}")
                raise e
                
    def post(self, url: str, **kwargs):
        """发送POST请求"""
        with allure.step(f"发送POST请求: {url}"):
            try:
                response: APIResponse = self.page.context.request.post(url, **kwargs)
                print(f"PASS|POST请求成功: {url}")
                return smart_response_handler(response)
            except Exception as e:
                print(f"FAIL|POST请求失败: {e}")
                raise e
                
    def put(self, url: str, **kwargs):
        """发送PUT请求"""
        with allure.step(f"发送PUT请求: {url}"):
            try:
                response: APIResponse = self.page.context.request.put(url, **kwargs)
                print(f"PASS|PUT请求成功: {url}")
                return smart_response_handler(response)
            except Exception as e:
                print(f"FAIL|PUT请求失败: {e}")
                raise e
                
    def patch(self, url: str, **kwargs):
        """发送PATCH请求"""
        with allure.step(f"发送PATCH请求: {url}"):
            try:
                response: APIResponse = self.page.context.request.patch(url, **kwargs)
                print(f"PASS|PATCH请求成功: {url}")
                return smart_response_handler(response)
            except Exception as e:
                print(f"FAIL|PATCH请求失败: {e}")
                raise e
                
    def delete(self, url: str, **kwargs):
        """发送DELETE请求"""
        with allure.step(f"发送DELETE请求: {url}"):
            try:
                response: APIResponse = self.page.context.request.delete(url, **kwargs)
                print(f"PASS|DELETE请求成功: {url}")
                return smart_response_handler(response)
            except Exception as e:
                print(f"FAIL|DELETE请求失败: {e}")
                raise e


def smart_response_handler(
    response: APIResponse,
    default_encoding: str = "utf-8"
) -> Union[dict, list, str, bytes]:
    """
    同步版本 - 智能处理响应内容，根据Content-Type自动选择最佳处理方法
    
    参数:
        response: Playwright的响应对象
        default_encoding: 当Content-Type未指定编码时的默认编码
        
    返回:
        根据内容类型解析后的数据:
        - JSON数据: Python字典或列表
        - 文本数据: 字符串
        - 二进制数据: 字节序列(bytes)
        - XML/HTML: 原始文本字符串
    """
    # 获取Content-Type头，默认为空字符串
    content_type = response.headers.get("content-type", "").lower()
    
    # 分离主类型和参数（如字符集）
    main_type = content_type.split(";")[0].strip()
    params = {}
    if ";" in content_type:
        for part in content_type.split(";")[1:]:
            if "=" in part:
                key, val = part.split("=", 1)
                params[key.strip()] = val.strip().strip('"')

    # 提取实际使用的编码
    encoding = params.get("charset", default_encoding)
    
    # 根据内容类型选择处理方法
    if main_type == "application/json":
        # 优先使用内置JSON解析
        try:
            return response.json()
        except json.JSONDecodeError:
            # JSON解析失败时使用文本作为后备
            text = response.text(encoding=encoding)
            # 尝试宽松解析（含JSONP）
            return parse_json_safe(text)
    
    elif main_type in ["text/html", "application/xhtml+xml", "application/xml", "text/xml"]:
        # HTML/XML内容作为文本返回
        return response.text(encoding=encoding)
    
    elif main_type.startswith("text/"):
        # 通用文本类型
        return response.text(encoding=encoding)
    
    elif main_type.startswith("image/") or main_type in [
        "application/octet-stream", 
        "application/pdf", 
        "application/zip",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"  # Excel
    ]:
        # 二进制数据类型
        return response.body()
    
    elif main_type == "multipart/form-data":
        # 多部分表单数据处理
        return handle_multipart(response, encoding)
    
    elif main_type == "application/x-www-form-urlencoded":
        # URL编码表单数据处理
        text = response.text(encoding=encoding)
        return parse_url_encoded_form(text)
    
    else:
        # 未知类型：尝试按文本处理，如果失败则返回二进制
        try:
            return response.text(encoding=encoding)
        except UnicodeDecodeError:
            return response.body()

def parse_json_safe(text: str) -> Union[dict, list, str]:
    """
    安全解析可能包含JSONP或无效JSON的文本
    
    返回:
        - 成功解析的JSON对象/数组
        - 如果无法解析则返回原始文本
    """
    # 尝试标准JSON解析
    try:
        return json.loads(text)
    except json.JSONDecodeError:
        pass
    
    # 尝试JSONP格式 (callback({...}))
    jsonp_pattern = r"^\w*?$(.*)$;?\s*$"
    jsonp_match = re.search(jsonp_pattern, text, re.DOTALL)
    if jsonp_match:
        try:
            return json.loads(jsonp_match.group(1))
        except json.JSONDecodeError:
            pass
    
    # 尝试包含错误前缀的情况 (如: while(1);{"data": ...})
    if text.startswith("while(1);") or text.startswith("for(;;);"):
        try:
            return json.loads(text.split(";", 1)[1])
        except (json.JSONDecodeError, IndexError):
            pass
    
    # 处理单引号JSON（非标准）
    try:
        # 安全替换单引号（只替换键和值的引号）
        corrected = re.sub(
            r"([{,]\s*)(['])(\w+)(['])(\s*:)", 
            r'\1"\3"\5', 
            text
        )
        return json.loads(corrected)
    except:
        pass
    
    # 所有尝试失败则返回原始文本
    return text

def parse_url_encoded_form(text: str) -> Dict[str, Union[str, List[str]]]:
    """
    解析URL编码的表单数据
    
    返回:
        包含表单数据的字典，支持多值字段
    """
    return parse.parse_qs(text)

def handle_multipart(response: APIResponse, encoding: str) -> Dict[str, Any]:
    """
    处理multipart/form-data响应（简化版）
    
    返回:
        包含表单数据的字典
    """
    # 在实际应用中，完整的multipart解析需要复杂逻辑
    # 这里简化为返回原始二进制内容
    content = response.body()
    return {"raw_multipart_data": content}